1 /* 2 * Copyright (C) 2012-2013 たかはしのんき. All rights reserved. 3 * 4 * History: 5 * 0.4 2013-06-17 プレイアウトの処理を実装。 6 * 0.3 2013-06-15 JsDoc Toolkit対応。メソッド showScore の作成。 7 * 0.2 2013-04-26 バージョンの変更。 8 * 0.1 2013-04-07 碁盤プログラム goban06.js を元に新規作成。 9 */ 10 11 /** 12 * @fileOverview Simulator - 囲碁対局シミュレータ 13 * @name simulator04.js 14 * @version 0.4 15 * @author たかはしのんき 16 * 17 */ 18 19 // 共通に使う定数と変数 20 var BLACK = 1; 21 var WHITE = 2; 22 var PX_BOWL = 134; // 碁笥の直径 [ピクセル] 23 var Z2_BOWL = 20; // 碁笥の高さ / 2 [ピクセル] 24 var PX_LID = 110; // 碁笥の蓋の直径 [ピクセル] 25 var Z2_LID = 8; // 碁笥の蓋の高さ / 2 [ピクセル] 26 var PX_STONE = 22; // 碁石の直径 [ピクセル] 27 var Z2_STONE = 2; // 碁石の高さ / 2 [ピクセル] 28 var PX_STAR = 5; // 星の直径 [ピクセル] 29 var PX_CHAR = 24; // 文字の高さ [ピクセル] 30 var Z2_BOARD = 6; // 碁盤の高さ / 2 [ピクセル] 31 32 // キャッシュにイメージを読み込んでおく 33 var imgbb = new Image(PX_BOWL, PX_BOWL); // 黒の碁笥 (black bowl) 34 imgbb.src = "img/blackbowl134.png"; 35 var imgwb = new Image(PX_BOWL, PX_BOWL); // 白の碁笥 (white bowl) 36 imgwb.src = "img/whitebowl134.png"; 37 var imgl = new Image(PX_LID, PX_LID); // 碁笥の蓋 (lid) 38 imgl.src = "img/lid110.png"; 39 var imgb = new Image(PX_STONE, PX_STONE); // 黒石 (white stone) 40 imgb.src = "img/black22.png"; 41 var imgw = new Image(PX_STONE, PX_STONE); // 白石 (black stone) 42 imgw.src = "img/white22.png"; 43 var imgs = new Image(PX_STAR, PX_STAR); // 星 (star) 44 imgs.src = "img/star5.png"; 45 46 // キャッシュに効果音を読み込んでおく 47 var ext = audioExt(); 48 var auClick = new Audio("se/button6." + ext); 49 var auSwitch = new Audio("se/se_sad05." + ext); 50 var auError = new Audio("se/se_sad08." + ext); 51 52 var ro = 9; // 路数 53 var context3; // 碁石レイヤーのキャンバスのコンテキスト 54 var context2; // 影レイヤーのキャンバスのコンテキスト 55 var passButton = new Array(2); // [パス]ボタン 56 var brd; // 碁盤オブジェクト 57 var offsetBLid = new Object(); // 黒の碁笥の蓋のオフセット 58 var offsetWLid = new Object(); // 白の碁笥の蓋のオフセット 59 60 /** 61 * ロード時に碁盤イメージを描画します。 62 */ 63 window.onload = function() { 64 // 黒石の <input id="black"> 取得 65 passButton[BLACK] = document.getElementById('black'); 66 // 白石の <input id="white"> 取得 67 passButton[WHITE] = document.getElementById('white'); 68 69 // キャンバスとコンテキスト取得 70 var canvas = document.querySelector('#table'); 71 var context = canvas.getContext('2d'); 72 var canvas3 = document.querySelector('#stones'); 73 context3 = canvas3.getContext('2d'); 74 var canvas2 = document.querySelector('#shadow'); 75 context2 = canvas2.getContext('2d'); 76 // 座標 (270, 50) にタイトルを表示 77 context.fillStyle = "white"; 78 context.font = "bold 24px Arial"; 79 var title = "Simulator 0.4"; 80 var x = context.canvas.width / 2 - 81 PX_CHAR / 1.8 * title.length / 2; 82 var y = PX_CHAR * 2; 83 context.fillText(title, x, y); 84 // 机の中央に 碁盤を表示 85 brd = new Board(ro, context); 86 // 碁盤の左に白の碁笥の蓋のイメージを表示 87 offsetWLid.x = Math.floor((brd.boardOffset.x - PX_LID) / 2); 88 offsetWLid.y = brd.boardOffset.y + 89 Math.floor((brd.boardSize.height - PX_LID) / 2); 90 drawObject(offsetWLid.x, offsetWLid.y, Z2_LID, imgl, context, context); 91 // 左上に白の碁笥のイメージを表示 92 var xb = offsetWLid.x - Math.floor((PX_BOWL - PX_LID) / 2); 93 var yb = offsetWLid.y - PX_BOWL - Z2_BOWL; 94 drawObject(xb, yb, Z2_BOWL, imgwb, context, context); 95 // 碁盤の右に黒の碁笥の蓋のイメージを表示 96 offsetBLid.x = offsetWLid.x + brd.boardOffset.x + brd.boardSize.width; 97 offsetBLid.y = offsetWLid.y; 98 drawObject(offsetBLid.x, offsetBLid.y, Z2_LID, imgl, context, context); 99 // 右下に黒の碁笥のイメージを表示 100 xb = offsetBLid.x - Math.floor((PX_BOWL - PX_LID) / 2); 101 yb = offsetBLid.y + PX_LID + Z2_BOWL; 102 drawObject(xb, yb, Z2_BOWL, imgbb, context, context); 103 // 碁盤を表示 104 brd.drawBoard(Z2_BOARD, context); 105 // 黒番から 106 passButton[WHITE].disabled = true; 107 // クリックイベント登録 108 canvas3.addEventListener("click", onClick, false); 109 }; 110 111 /** 112 * アゲハマの表示 113 * @param {number} stone 石の色 114 * @param {number} prisoner アゲハマの数 115 * @since 0.1 116 */ 117 function showPrisoner(stone, prisoner) { 118 if (stone == BLACK) { 119 var p = document.getElementById('pb'); 120 } else { 121 var p = document.getElementById('pw'); 122 } 123 p.firstChild.nodeValue = prisoner; 124 } 125 126 /** 127 * 得点の表示 128 * @param {number} stone 石の色 129 * @param {number} score 得点 130 * @since 0.3 131 */ 132 function showScore(stone, score) { 133 if (stone == BLACK) { 134 var p = document.getElementById('sb'); 135 } else { 136 var p = document.getElementById('sw'); 137 } 138 p.firstChild.nodeValue = score; 139 } 140 141 /** 142 * マウスイベントハンドラー 143 * 碁石を置きます。 144 * @since 0.1 145 */ 146 function onClick() { 147 var event = arguments[0]; 148 var mx = event.pageX - this.parentNode.offsetLeft; 149 var my = event.pageY - this.parentNode.offsetTop; 150 var mv = brd.offsetToMove(mx, my); 151 if (mv.onBoard) { 152 brd.move(mv.col, mv.row, context3, context2); 153 } else { 154 if (brd.p.turn == BLACK) 155 drawObject(mx - (PX_STONE / 2), my - (PX_STONE / 2), Z2_STONE, imgb, context2, context2); 156 else 157 drawObject(mx - (PX_STONE / 2), my - (PX_STONE / 2), Z2_STONE, imgw, context2, context2); 158 auError.play(); 159 } 160 } 161 162 163 /** 164 * [パス]ボタンの処理 165 * @since 0.1 166 */ 167 function pass() { 168 // ボタンの音を鳴らす。 169 auSwitch.play(); 170 var col = PASS(ro); 171 var row = PASS(ro); 172 brd.move(col, row, context3, context2); 173 } 174 175 /** 176 * [プレイアウト]ボタンの処理 177 * @since 0.4 178 */ 179 function playout() { 180 // ボタンの音を鳴らす。 181 auSwitch.play(); 182 // 碁石と影のレイヤーをクリア 183 context3.clearRect(0, 0, context3.canvas.width, context3.canvas.height); 184 context2.clearRect(0, 0, context2.canvas.width, context2.canvas.height); 185 // アゲハマのクリア 186 showPrisoner(BLACK, 0); 187 showPrisoner(WHITE, 0); 188 // 得点のクリア 189 showScore(BLACK, 0); 190 showScore(WHITE, 0); 191 // 黒番から 192 brd.clear(); 193 passButton[BLACK].disabled = false; 194 passButton[WHITE].disabled = true; 195 // プレイアウト 196 var lastPass = false; 197 var timer = window.setInterval(function() { 198 if (0 < brd.p.c.abs()) { 199 // 着手可能な手があればその中からランダムに1手選んで打つ 200 var i = Math.floor(Math.random() * brd.p.order) + 1; 201 for ( ; ; i++) { 202 if (brd.p.order < i) 203 i = 1; 204 if (brd.p.c.getValue(i) == 1) 205 break; 206 } 207 var mv = brd.p.toMove(i) 208 brd.move(mv.col, mv.row, context3, context2); 209 lastPass = false; 210 } else { 211 // なければパス 212 auSwitch.play(); 213 brd.move(PASS(brd.p.ro), PASS(brd.p.ro), context3, context2); 214 // 両者パスならば終局 215 if (lastPass) 216 window.clearInterval(timer); 217 lastPass = true; 218 } 219 }, 300); // 300ms間隔で次の手を打つ 220 } 221 222 /** 223 * ブラウザで有効なAudioの拡張子を獲得 224 * @since 0.1 225 */ 226 function audioExt() { 227 var ext = ""; 228 var audio = new Audio(); 229 if (audio.canPlayType("audio/ogg") == 'maybe') 230 ext="ogg"; 231 else if (audio.canPlayType("audio/mp3") == 'maybe') 232 ext="mp3"; 233 else if (audio.canPlayType("audio/wav") == 'maybe') 234 ext="wav"; 235 return ext; 236 } 237 238 /** 239 * 円形オブジェクトを描画します。 240 * @param x 左端座標 241 * @param y 上端座標 242 * @param s 影の長さ(高さ/2) 243 * @param img オブジェクトのイメージ 244 * @param context3 描画先のコンテキストを指定します。 245 * @param context2 影用のコンテキストを指定します。 246 * @since 0.1 247 */ 248 function drawObject(x, y, s, img, context3, context2) { 249 var r = img.width / 2; 250 context2.beginPath(); 251 context2.arc(x + r + s, y + r + s, r, 0, 2 * Math.PI); 252 context2.fillStyle = "black"; 253 context2.globalAlpha = 0.5; 254 context2.fill(); 255 context3.globalAlpha = 1.0; 256 context3.drawImage(img, x, y); 257 } 258